package com.saer.server.service.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Date;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.hibernate.Hibernate;

import com.saer.client.constants.ClientConstants;
import com.saer.client.constants.SaerProperties;
import com.saer.client.dto.diagnostico.VisitaDto;
import com.saer.client.dto.network.MsgDto;
import com.saer.server.hibernate.dao.VisitaDao;
import com.saer.server.hibernate.model.Cap;
import com.saer.server.hibernate.model.Enfermero;
import com.saer.server.hibernate.model.Paciente;
import com.saer.server.hibernate.model.Visita;
import com.saer.server.network.CapServer;
import com.saer.server.network.ExpiringQueue;
import com.saer.server.network.electro.Electrocardiograma;
import com.saer.server.network.logica.mensajes.MensajeDatos;
import com.saer.server.network.logica.mensajes.MensajeTexto;
import com.saer.server.service.NetworkService;

public class NetworkServiceImpl implements NetworkService {

	private SaerProperties saerProperties;
	private static VisitaDao visitaDao;
	private static ConcurrentHashMap<Integer, ExpiringQueue<MsgDto>> visitasEntrantes = new ConcurrentHashMap<Integer, ExpiringQueue<MsgDto>>();
	private static ConcurrentHashMap<Integer, Integer> activeCaps = new ConcurrentHashMap<Integer, Integer>();
	private static ConcurrentHashMap<Integer, Integer> activeHospitals = new ConcurrentHashMap<Integer, Integer>();
	
	private static CapServer waitingVisitas = null;
	private static ConcurrentHashMap<Integer, Integer> chacras = new ConcurrentHashMap<Integer, Integer>();
	
	public void setVisitaDao(VisitaDao visitaDao) {
		NetworkServiceImpl.visitaDao = visitaDao;
	}

	public void esperaVisita() {
		if (waitingVisitas == null) {
			waitingVisitas = new CapServer(this);
		}
		if (!waitingVisitas.isAlive()) {
			waitingVisitas.start();
		}
	}

	public MsgDto endPoll(int clientId, int centroMedicoType, int centroMedicoId) {
		return endPoll(clientId, centroMedicoType, centroMedicoId, null);
	}
	
	public MsgDto endPoll(int clientId, int centroMedicoType, int centroMedicoId, String message) {
		
		if (centroMedicoType == ClientConstants.CAP) {
			activeCaps.remove(centroMedicoId);
		} else {
			activeHospitals.remove(centroMedicoId);
		}
		
		BlockingQueue<MsgDto> clientQueue = visitasEntrantes.remove(clientId);
		
		if (clientQueue != null) {
			try {
				MsgDto newMsg = new MsgDto(clientId, ClientConstants.OP_POLL_END, message);
				clientQueue.put(newMsg);
				return newMsg;
			} catch (InterruptedException e) {
				e.printStackTrace();
				return new MsgDto(clientId, ClientConstants.OP_POLL_ERROR, e.getMessage());
			}
		}
		
		return new MsgDto(clientId, ClientConstants.OP_POLL_ERROR, "Error grave finalizando poll: No se encontro el cliente [" + clientId + "]");
		
	}
	
	public List<VisitaDto> getVisitas() {
		List<VisitaDto> result = new ArrayList<VisitaDto>();
		
		for (Object o : visitaDao.getVisitas()) {
			Visita visita = (Visita) o;
		
			result.add(new VisitaDto(
					visita.getId(),
					visita.getPaciente().getDni(),
					visita.getPaciente().getNombre(),
					visita.getPaciente().getApellido(),
					visita.getPaciente().getEdad(),
					visita.getPaciente().isSexo(),
					visita.getEnfermero().getId(),
					visita.getEnfermero().getNombre(),
					visita.getEnfermero().getApellido(),
					visita.getEnfermero().getEmail(),
					visita.getCap().getId(),
					visita.getCap().getDescripcion(),
					visita.getFecha(),
					visita.getTemperatura() != null ? visita.getTemperatura().doubleValue() : null,
					visita.getPresion_Sistolica(),					
					visita.getPresion_Distolica(),
					visita.getFrespiratoria(),
					visita.getPulsaciones(),
					visita.getGlucosaSangre(),
					visita.getOxigenoSangre(),
					visita.getSintomas(),
					visita.getEstado()));
		}

		return result;
	}
	
	public MsgDto getNewVisita(int clientId, int centroMedicoType, int centroMedicoId) {
		try {
			ExpiringQueue<MsgDto> clientQueue = visitasEntrantes.get(clientId);
			
			if (clientQueue == null) {
				if (centroMedicoType == ClientConstants.CAP) {
					if (activeCaps.containsKey(centroMedicoId)) {
						endPoll(activeCaps.get(centroMedicoId), centroMedicoType, centroMedicoId, "Se ha abierto una nueva instancia del CAP [" + centroMedicoId + "], la actual se ha cerrado.");
					}
					activeCaps.put(centroMedicoId, clientId);
				} else {
					if (activeHospitals.containsKey(centroMedicoId)) {
						endPoll(activeHospitals.get(centroMedicoId), centroMedicoType, centroMedicoId, "Se ha abierto una nueva instancia del Hospital [" + centroMedicoId + "], la actual se ha cerrado.");
					}
					activeHospitals.put(centroMedicoId, clientId);
				}
				clientQueue = new ExpiringQueue<MsgDto>(5);
				visitasEntrantes.put(clientId, clientQueue);
			}

			clientQueue.resetRemainigLives();
			
			MsgDto newMsg = clientQueue.poll(1, TimeUnit.MINUTES);

			if (newMsg != null) {
				return newMsg;
			}
			
		} catch (InterruptedException e) {
			e.printStackTrace();
			return new MsgDto(clientId, ClientConstants.OP_POLL_ERROR, e.getMessage());
		}
		return new MsgDto(clientId, ClientConstants.OP_POLL_AGAIN);
	}
	
	public void insertarVisita(MensajeDatos mac) {
		Enfermero enfermero = visitaDao.getEnfermero(mac.getEnfermero().getId());
		Paciente paciente = visitaDao.getPaciente(String.valueOf(mac.getPaciente().getDNI()));
		if (enfermero != null) {
			if (paciente == null) {
				paciente = new Paciente(
						0,
						String.valueOf(mac.getPaciente().getDNI()),
						mac.getPaciente().getNombre(),
						mac.getPaciente().getApellido(),
						mac.getPaciente().getEdad(),
						mac.getPaciente().getSexo().equals("F") ? true : false);
				
				visitaDao.insertarPaciente(paciente);

			}
			
		    ByteArrayOutputStream ba = new ByteArrayOutputStream();
		    ObjectOutputStream oos;
			try {
				oos = new ObjectOutputStream(ba);
				oos.writeObject(mac.getElectro());
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		   
		    byte[] arrayBytes = ba.toByteArray();
		    
		    Blob ecg = null;
		    
	    
		    ecg = Hibernate.createBlob(arrayBytes);

			
			Visita visita = new Visita(0,
					paciente,
					enfermero,
					new Date(new java.util.Date().getTime()),
					new BigDecimal(mac.getPaciente().getTemperatura()),
					mac.getPaciente().getPresion_sistolica(),
					mac.getPaciente().getPresion_distolica(),					
					mac.getPaciente().getFrecuencia_resp(),
					mac.getPaciente().getPpm(),
					mac.getPaciente().getGlucosa_en_sangre(),
					mac.getPaciente().getOxigeno_en_sangre(),
					mac.getCuerpo(),
					new Cap(mac.getIDdestino()),
					0, ecg);
			
			Integer id = visitaDao.insertarVisita(visita);

			if (id != null) {
				try {
					visita.setId(id);
					
					chacras.put(id, mac.getIDorigen());
					
					for (Integer clientId : visitasEntrantes.keySet()) {
						visitasEntrantes.get(clientId).put(new MsgDto(clientId, ClientConstants.OP_NEW_VISITA));
					}
					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public MsgDto sendToChacra(int visitaId, int capId, String text) {
		MensajeTexto msj = new MensajeTexto();
		msj.setCuerpo(text);
		msj.setIDorigen(capId);

		Integer chacraId = chacras.get(visitaId);
		
		if (chacraId != null) {
			try {
				msj.setIDdestino(chacraId);
				waitingVisitas.sendMsjTxt(chacraId, msj);
				return new MsgDto(ClientConstants.OP_SEND_MSG_OK);
			} catch (IOException e) {
				e.printStackTrace();
				return new MsgDto(ClientConstants.OP_SEND_MSG_ERROR, "Fallo el envio de la respuesta: " + e.getMessage());
			}
		} else {
			return new MsgDto(ClientConstants.OP_SEND_MSG_ERROR, "Fallo el envio de la respuesta: No se encontro la Chacra.");
		}
	}

	public static Electrocardiograma getEcg(int id) {
		
		Visita visita = visitaDao.getVisita(id);	
		Blob blob = visita.getEcg();
		int blobLength;
	    Electrocardiograma ecg = null;
		try {
			blobLength = (int) blob.length();
			byte[] blobAsBytes = blob.getBytes(1, blobLength);
			ByteArrayInputStream bai=new ByteArrayInputStream(blobAsBytes);               
		    
		    ObjectInputStream ois=new ObjectInputStream(bai);
		    ecg = (Electrocardiograma) ois.readObject(); 

		} catch (SQLException e) {
			
			e.printStackTrace();
		} catch (IOException e) {
			
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			
			e.printStackTrace();
		}  
	    
		return ecg;
	}
	
	
	public static ConcurrentHashMap<Integer, ExpiringQueue<MsgDto>> getVisitasEntrantes() {
		return visitasEntrantes;
	}

	public void agregar_mensaje(MensajeDatos m) {
		this.insertarVisita(m);
	}

	public void setSaerProperties(SaerProperties saerProperties) {
		this.saerProperties = saerProperties;
	}

	public SaerProperties getSaerProperties() {
		return saerProperties;
	}
	
}
